﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NumericalCalculations
{
    public static class Elementary
    {
        private static uint[] b = new uint[] { 
        0xa2f983, 0x6e4e44, 0x1529fc, 0x2757d1, 0xf534dd, 0xc0db62, 0x95993c, 0x439041, 0xfe5163, 0xabdebb, 0xc561b7, 0x246e3a, 0x424dd2, 0xe00649, 0x2eea09, 0xd1921c, 
        0xfe1deb, 0x1cb129, 0xa73ee8, 0x8235f5, 0x2ebb44, 0x84e99c, 0x7026b4, 0x5f7e41, 0x3991d6, 0x398353, 0x39f49c, 0x845f8b, 0xbdf928, 0x3b1ff8, 0x97ffde, 0x5980f, 
        0xef2f11, 0x8b5a0a, 0x6d1f6d, 0x367ecf, 0x27cb09, 0xb74f46, 0x3f669e, 0x5fea2d, 0x7527ba, 0xc7ebe5, 0xf17b3d, 0x739f7, 0x8a5292, 0xea6bfb, 0x5fb11f, 0x8d5d08
     };



        internal static DoubleComplex a(DoubleComplex A_0)
        {
            return new DoubleComplex(b1(A_0.Re), b1(A_0.Im));
        }

        internal static double a(double A_0)
        {
            return (A_0 % 6.2831853071795862);
        }

        private static double a(int A_0)
        {
            ulong num2;
            A_0--;
            int num = A_0 + 0x18;
            if (num <= 0)
            {
                return 0.0;
            }
            if (A_0 < 0)
            {
                num2 = b[0] >> (0x18 - num);
            }
            else
            {
                int index = A_0 / 0x18;
                int num4 = A_0 - (index * 0x18);
                if (num4 == 0)
                {
                    num2 = b[index];
                }
                else
                {
                    num2 = ((b[index] << num4) + (b[index + 1] >> (0x18 - num4))) & 0xffffff;
                }
            }
            int num5 = num;
            double num6 = 1.0;
            if (num5 < 0x3ff)
            {
                num6 = BitConverter.Int64BitsToDouble((0x3ffL - num5) << 0x34);
            }
            else if (num5 <= 0x432)
            {
                num6 = BitConverter.Int64BitsToDouble(((long)1L) << (0x432 - num5));
            }
            else
            {
                num6 = 0.0;
            }
            return (num2 * num6);
        }

        internal static double a(double A_0, out int A_1)
        {
            if (Math.Abs(A_0) <= 16777216.0)
            {
                double num = Math.Round((double)(A_0 * 0.63661977236758138));
                A_0 -= num * 1.5707963109016419;
                A_0 -= num * 1.5893254712295857E-08;
                A_0 -= num * 6.123233995736766E-17;
                A_1 = ((int)num) & 3;
                return A_0;
            }
            if (IsNaN(A_0))
            {
                A_1 = -1;
                return double.NaN;
            }
            A_1 = 0;
            int num3 = ((int)((BitConverter.DoubleToInt64Bits(A_0) >> 0x34) & 0x7ffL)) - 0x3ff;
            if (num3 < 1)
            {
                A_1 = 0;
                return A_0;
            }
            int num4 = num3 - 0x34;
            double num5 = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(A_0) & -16777216L);
            A_0 -= num5;
            double num6 = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(A_0) & -16777216L);
            double num7 = A_0 - num6;
            double num8 = a((int)(num4 - 1));
            double num9 = a((int)(num4 + 0x17));
            double num10 = a((int)(num4 + 0x2f));
            double num11 = a((int)(num4 + 0x47));
            double num12 = a((int)(num4 + 0x5f));
            double num13 = (num8 * num5) % 4.0;
            double num14 = (num9 * num5) % 4.0;
            double num15 = num10 * num5;
            double num16 = num11 * num5;
            double num17 = num12 * num5;
            double num18 = 0.0;
            if (num6 != 0.0)
            {
                num14 += num8 * num6;
                num15 += num9 * num6;
                num16 += num10 * num6;
                num17 += num11 * num6;
                num18 += num12 * num6;
            }
            if (num7 != 0.0)
            {
                num15 += num8 * num7;
                num16 += num9 * num7;
                num17 += num10 * num7;
                num18 += num11 * num7;
            }
            num13 = num13 % 4.0;
            num14 = num14 % 4.0;
            num15 = num15 % 4.0;
            num16 = num16 % 4.0;
            num17 = num17 % 4.0;
            num18 = num18 % 4.0;
            double num19 = num13 + (num14 + (num15 + (num16 + (num17 + num18))));
            double num20 = (((((num13 - num19) + num14) + num15) + num16) + num17) + num18;
            if (Math.Abs(num20) < 2.2204460492503131E-16)
            {
                double num21 = a((int)(num4 + 0x77));
                num18 += num21 * num5;
                double num22 = (num21 * num6) + (num12 * num7);
                double num23 = a((int)(num4 + 0x8f));
                num22 += num23 * num5;
                num19 = num13 + (num14 + (num15 + (num16 + (num17 + (num18 + num22)))));
                num20 = ((((((num13 - num19) + num14) + num15) + num16) + num17) + num18) + num22;
            }
            A_1 = (int)(num19 + 0.5);
            num19 = (num19 - ((double)A_1)) + num20;
            A_1 &= 3;
            return (num19 * 1.5707963267948966);
        }

        private static double a(double A_0, int A_1)
        {
            switch (A_1)
            {
                case 0:
                    return 1.0;

                case 1:
                    return A_0;

                case 4:
                    A_0 *= A_0;
                    return (A_0 * A_0);

                case 5:
                    {
                        double num = A_0 * A_0;
                        return ((num * num) * A_0);
                    }
            }
            if (A_0 == 1.0)
            {
                return A_0;
            }
            bool flag = false;
            if (A_1 < 0)
            {
                flag = true;
                A_1 = Math.Abs(A_1);
            }
            if (A_0 == 0.0)
            {
                if (flag)
                {
                    return (1.0 / A_0);
                }
                return 0.0;
            }
            if (A_0 == 1.0)
            {
                return A_0;
            }
            if (A_0 == -1.0)
            {
                if ((A_1 & 1) != 0)
                {
                    return -1.0;
                }
                return 1.0;
            }
            double num2 = A_0;
            double num3 = 1.0;
            do
            {
                if ((A_1 & 1) != 0)
                {
                    num3 *= num2;
                }
                num2 *= num2;
                A_1 = A_1 >> 1;
            }
            while (A_1 > 0);
            if (flag)
            {
                return (1.0 / num3);
            }
            return num3;
        }

        public static double Acosh(double x)
        {
            if (x < 1.0)
            {
                return double.NaN;
            }
            return Math.Log(x + Math.Sqrt((x * x) - 1.0));
        }

        public static double Acoth(double x)
        {
            if ((x > -1.0) && (x < 1.0))
            {
                return double.NaN;
            }
            if (Math.Abs(x) > 5.0)
            {
                return (0.5 * Log1PlusX(2.0 / (x - 1.0)));
            }
            return (0.5 * Math.Log(1.0 + (2.0 / (x - 1.0))));
        }

        public static double Acsch(double x)
        {
            return Math.Log((1.0 / x) + Math.Sqrt(1.0 + (1.0 / (x * x))));
        }

        public static double Asech(double x)
        {
            if ((x > 0.0) && (x <= 1.0))
            {
                return Math.Log((1.0 / x) + Math.Sqrt((1.0 / (x * x)) - 1.0));
            }
            return double.NaN;
        }

        public static double Asinh(double x)
        {
            return Math.Log(x + Math.Sqrt((x * x) + 1.0));
        }

        public static double Atanh(double x)
        {
            if ((x < -1.0) || (x > 1.0))
            {
                return double.NaN;
            }
            if (Math.Abs(x) == 1.0)
            {
                if (x <= 0.0)
                {
                    return double.NegativeInfinity;
                }
                return double.PositiveInfinity;
            }
            if (Math.Abs(x) < 0.2)
            {
                return (0.5 * Log1PlusX((2.0 * x) / (1.0 - x)));
            }
            return (0.5 * Math.Log(1.0 + ((2.0 * x) / (1.0 - x))));
        }

        internal static double b1(double A_0)
        {
            if (!IsFinite(A_0))
            {
                return double.NaN;
            }
            return 0.0;
        }

        public static double Clip(double lowerBound, double upperBound, double x)
        {
            return Math.Max(lowerBound, Math.Min(upperBound, x));
        }

        public static double Cos(double angle)
        {
            int num;
            angle = Math.Abs(angle);
            if (angle < 6.2831853071795862)
            {
                return Math.Cos(angle);
            }
            angle = a(angle, out num);
            switch (num)
            {
                case 0:
                    return Math.Cos(angle);

                case 1:
                    return -Math.Sin(angle);

                case 2:
                    return -Math.Cos(angle);

                case 3:
                    return Math.Sin(angle);
            }
            return double.NaN;
        }

        public static double Cosh(double x)
        {
            return Math.Cosh(x);
        }

        public static double CosPi(double x)
        {
            x -= 2.0 * Math.Round((double)(x / 2.0));
            if (Math.Abs(x) == 1.0)
            {
                return -1.0;
            }
            if (Math.Abs(x) == 0.5)
            {
                return 0.0;
            }
            return Math.Cos(x * 3.1415926535897931);
        }

        public static double Coth(double x)
        {
            return (1.0 / Math.Tanh(x));
        }

        public static double Csch(double x)
        {
            return (1.0 / Math.Sinh(x));
        }

        public static double ExpMinus1(double x)
        {
            if ((x < -0.69314718055994529) || (x > 0.4054651081081645))
            {
                return (Math.Exp(x) - 1.0);
            }
            if (IsNaN(x))
            {
                return x;
            }
            double num = Math.Tanh(0.5 * x);
            return ((2.0 * num) / (1.0 - num));
        }

        public static double Hypot(double a, double b)
        {
            if (!IsFinite(a))
            {
                if (IsInfinite(b))
                {
                    return Math.Abs(b);
                }
                return Math.Abs(a);
            }
            if (!IsFinite(b))
            {
                return Math.Abs(b);
            }
            if (a == 0.0)
            {
                return Math.Abs(b);
            }
            if (b == 0.0)
            {
                return Math.Abs(a);
            }
            a = Math.Abs(a);
            b = Math.Abs(b);
            if (a > b)
            {
                double num = b / a;
                return (a * Math.Sqrt(1.0 + (num * num)));
            }
            double num2 = a / b;
            return (b * Math.Sqrt(1.0 + (num2 * num2)));
        }

        public static bool IsFinite(double x)
        {
            return (((int)((BitConverter.DoubleToInt64Bits(x) >> 0x20) & 0x7ff00000L)) < 0x7ff00000);
        }

        public static bool IsInfinite(double x)
        {
            return ((BitConverter.DoubleToInt64Bits(x) & 0x7fffffffffffffffL) == 0x7ff0000000000000L);
        }

        public static bool IsNaN(double x)
        {
            return ((BitConverter.DoubleToInt64Bits(x) & 0x7fffffffffffffffL) > 0x7ff0000000000000L);
        }

        public static double LambertW(double x)
        {
            double num7;
            if ((x * 2.7182818284590451) < -1.0)
            {
                throw new ArgumentOutOfRangeException("x");
            }
            double d = 0.0;
            if (x < 0.0)
            {
                double num2 = 1.4142135623730952 * Math.Sqrt(1.0 + (2.7182818284590451 * x));
                d = ((0.50000000254110366 + (num2 * (2.077082690400045 + (num2 * (1.4275043714348843 + (num2 * 0.18169293085614638)))))) / (1.0 + (num2 * (1.3257391022094323 + (num2 * (0.43856420102046717 + (num2 * 0.0265500411085822))))))) - 1.5;
            }
            if (x < 0.3)
            {
                d = (0.99999998958768466 + (x * (2.0367969407614619 + (x * (0.40989120652325517 - (x * 0.055180135629034427)))))) / (1.0 + (x * (3.036793688437895 + (x * 1.9468487052703809))));
            }
            else if (x < 2.3)
            {
                d = (6.91370020636232E-05 + (x * (0.99891196507386126 + (x * (1.6699417741103892 + (x * (0.45463995685440306 + (x * 0.0062805649158445319)))))))) / (1.0 + (x * (2.661987382297955 + (x * (1.6538505709215219 + (x * 0.20277327219972122))))));
            }
            else if (x < 10.3)
            {
                d = (0.01976952864697059 + (x * (0.91255159181269541 + (x * (0.45556098974402293 + (x * (0.030507319010741406 + (x * 7.9583704602728445E-05)))))))) / (1.0 + (x * (1.2250936530860326 + (x * (0.26576871286495957 + (x * 0.0094435537295920469))))));
            }
            else if (x < 25000.0)
            {
                double num3 = (x - 13000.0) / 12000.0;
                d = (7.463270151357106 + (num3 * (11.012438386596848 + (num3 * 3.8836808096898814)))) / (1.0 + (num3 * (1.3667593539934748 + (num3 * (0.42189887531405906 - (num3 * 0.0085760410974559369))))));
            }
            else
            {
                double num4 = Math.Log(x);
                double num5 = 1.0 / num4;
                double num6 = Math.Log(num4);
                d = (num4 - num6) + ((num6 * num5) * (1.0 + (num5 * ((0.5 * (num6 - 2.0)) + (num5 * (6.0 + (num6 * ((2.0 * num6) - 9.0))))))));
            }
            do
            {
                double num8 = Math.Exp(d);
                num7 = ((d * num8) - x) / ((d + 1.0) * num8);
                d -= num7;
            }
            while (Math.Abs(num7) > 1E-10);
            return d;
        }

        public static double Log1PlusX(double x)
        {
            if (IsNaN(x))
            {
                return x;
            }
            if (x <= -1.0)
            {
                throw new ArgumentOutOfRangeException("x");
            }
            double d = 1.0 + x;
            if (d == 1.0)
            {
                return x;
            }
            double num2 = x * x;
            if (num2 < 6.0554544523933395E-06)
            {
                if (num2 < 1E-12)
                {
                    return (x * (1.0 + (x * (-0.5 + (x * 0.33333333333333331)))));
                }
                return (x * (1.0 + (x * (-0.5 + (x * (0.33333333333333331 + (x * (-0.25 + (x * (0.2 + (x * (-0.16666666666666666 + (x * (0.14285714285714285 + (x * 0.125)))))))))))))));
            }
            double num3 = d - 1.0;
            return (Math.Log(d) * (x / num3));
        }

        public static double Pow(double x, int n)
        {
            if (n == 2)
            {
                return (x * x);
            }
            if (n == 3)
            {
                return ((x * x) * x);
            }
            return a(x, n);
        }

        public static double Sech(double x)
        {
            return (1.0 / Math.Cosh(x));
        }

        public static double Sin(double angle)
        {
            int num;
            if (Math.Abs(angle) < 6.2831853071795862)
            {
                return Math.Sin(angle);
            }
            angle = a(angle, out num);
            switch (num)
            {
                case 0:
                    return Math.Sin(angle);

                case 1:
                    return Math.Cos(angle);

                case 2:
                    return -Math.Sin(angle);

                case 3:
                    return -Math.Cos(angle);
            }
            return double.NaN;
        }

        public static double Sinc(double x)
        {
            if (x == 0.0)
            {
                return 1.0;
            }
            if (IsNaN(x))
            {
                return x;
            }
            return (Math.Sin(3.1415926535897931 * x) / (3.1415926535897931 * x));
        }

        public static double Sinh(double x)
        {
            return Math.Sinh(x);
        }

        public static double SinPi(double x)
        {
            x -= 2.0 * Math.Round((double)(x / 2.0));
            if (Math.Abs(x) == 1.0)
            {
                return 0.0;
            }
            if (Math.Abs(x) != 0.5)
            {
                return Math.Sin(x * 3.1415926535897931);
            }
            if (x <= 0.0)
            {
                return -1.0;
            }
            return 1.0;
        }

        public static double Sqrt1pxm1(double x)
        {
            double num = Math.Sqrt(1.0 + x) - 1.0;
            if (num < 0.5)
            {
                num += (0.5 * (x - (num * (num + 2.0)))) / (num + 1.0);
            }
            return num;
        }

        public static double Step(double x)
        {
            if (x >= 0.0)
            {
                return 1.0;
            }
            if (x < 0.0)
            {
                return 0.0;
            }
            return x;
        }

        public static double Step(double x0, double x)
        {
            if (x >= x0)
            {
                return 1.0;
            }
            if (x < x0)
            {
                return 0.0;
            }
            return (x + x0);
        }

        public static double Tan(double angle)
        {
            int num;
            if (Math.Abs(angle) < 6.2831853071795862)
            {
                return Math.Tan(angle);
            }
            angle = a(angle, out num);
            double num2 = Math.Sin(angle);
            double num3 = Math.Cos(angle);
            if (num < 0)
            {
                return double.NaN;
            }
            if ((num & 1) == 0)
            {
                return (num2 / num3);
            }
            return (-num3 / num2);
        }

        public static double Tanh(double x)
        {
            return Math.Tanh(x);
        }

        public static double TanPi(double x)
        {
            x -= 2.0 * Math.Round((double)(x / 2.0));
            if (Math.Abs(x) == 1.0)
            {
                return 0.0;
            }
            if (Math.Abs(x) != 0.5)
            {
                return Math.Tan(x * 3.1415926535897931);
            }
            if (x <= 0.0)
            {
                return double.NegativeInfinity;
            }
            return double.PositiveInfinity;
        }

       


    }
}
